import json
import re
import threading
import traceback
from functools import wraps
from time import sleep
import time

import redis
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QFileDialog
from playsound import playsound
from pymysql import cursors

from Config.Params import DBCNF_DIF
from Service.MyLogger import MyLogger
import os,logging,sqlite3

from Sql.DbCursorR import DbCursorR
from Utils import DirUtil, SqliteUtill

logFileName =  os.path.basename(__file__).split(".")[0]
from Statistics.COMMON import commonLogger
log = commonLogger # MyLogger(r"./Logs/%s.log" % (logFileName), logging.DEBUG, logging.DEBUG)
def getDbConnByFile(filePath:str):
    with open(DirUtil.upperIfNotExists(filePath),"r",encoding='utf-8',errors='ignore') as f:
        ss = f.read()
    ss = ss.replace("\n","").replace(" ","").replace("}","}**").strip("**")
    arr = re.split("\*\*",ss)
    dictt = {}
    for i in range(len(arr)):
        k,v = arr[i].split("=")
        dictt[k]=v
    dictt["rsConn"] = dictt["rsConn"].replace("sqlConn",dictt["sqlConn"])
    dictt["rsConn"] = dictt["rsConn"].replace("rdsConn", dictt["rdsConn"])
    dictt["dbConnect"] = dictt["dbConnect"].replace("rsConn", dictt["rsConn"])
    dictt["dbConnect"] = dictt["dbConnect"].replace("dbXX", dictt["dbXX"])
    for k,v in dictt.items():
        dictt[k] = json.loads(v)
    print('dictt["dbConnect"]:',dictt["dbConnect"])
    dbConn = dictt["dbConnect"]
    return dbConn
def getDbCursorRByDbConn(dbConn):
    dbCursorR = DbCursorR(dbConn)
    return dbCursorR
def getDbCursorRByFile(filePath:str):
    dbConn = getDbConnByFile(filePath)
    return getDbCursorRByDbConn(dbConn)


def ShowTimeCost(a_func,cd=-5):
    print(cd)
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        t0 = time.time()
        res = a_func(*args, **kwargs)
        t9 = time.time()
        log.info(a_func.__name__, "timeCost:", round(t9 - t0, 2))
        return res

    return wrapTheFunction

def TaskChain(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        print("args:",args)
        print("kwargs:",kwargs)
        try:
            res = a_func(*args, **kwargs)
        except:
            res = "task error: "+traceback.format_exc()
        print(res)
        # args[0].append(res)
        if 'chainLog' in kwargs:
            kwargs['chainLog'].append({a_func.__name__: res})
        return res
    return wrapTheFunction

def RetryIfIs(countDown=3, slpSeconds=5, retryIfIs=None):
    def RetryB1(a_func):
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            for i in range(countDown):
                try:
                    res = a_func(*args, **kwargs)
                    if res == retryIfIs:
                        print("Not expected I", "countDown:", countDown-i)
                        sleep(slpSeconds)
                    else:
                        return res
                except:
                    log.info("yzlog traceback RetryIfIs:", traceback.format_exc())
                    print("Abnormal", "countDown:", countDown-i)
                    sleep(slpSeconds)
            return None
        return wrapTheFunction
    return RetryB1
def RetryIfNot(countDown=3, slpSeconds=5, retryIfNot=None):
    def RetryB1(a_func):
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            for i in range(countDown):
                try:
                    res = a_func(*args, **kwargs)
                    if res != retryIfNot:
                        print("Not expected", "countDown:", countDown - i)
                        sleep(slpSeconds)
                    else:
                        return res
                except:
                    log.info("yzlog traceback RetryIfNot:", traceback.format_exc())
                    print("Abnormal", "countDown:", countDown-i)
                    sleep(slpSeconds)
            return None
        return wrapTheFunction
    return RetryB1
def WithSqlite(a_func):
    try:
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            conn = sqlite3.connect(DirUtil.getDapDir())
            c = conn.cursor()
            res = a_func(c=c, *args, **kwargs)
            print("res:",res)
            # print(res)
            conn.commit()
            conn.close()
            return res
        return wrapTheFunction
    except:
        log.error("yzlog traceback withSqlite:", traceback.format_exc())
def WithCertainSqlite(connPath:str=None):
    def B1(a_func):
        try:
            @wraps(a_func)
            def wrapTheFunction(conn=None, c=None, *args, **kwargs):
                if conn is None or c is None:
                    conn = sqlite3.connect(
                        DirUtil.getDefaultDb() if connPath is None else DirUtil.upperIfNotExists(connPath))
                    c = conn.cursor()
                res = a_func(conn=None, c=c, *args, **kwargs)
                print("res:",res)
                conn.commit()
                conn.close()
                return res
            return wrapTheFunction
        except:
            log.error("yzlog traceback withSqlite:", traceback.format_exc())
    return B1
def WithCertainMySql(connFilePath:str=None):
    def B1(a_func):
        try:
            @wraps(a_func)
            def wrapTheFunction(db=None, c=None, *args, **kwargs):
                if db is None or c is None:
                    dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
                    dbCursorR = DbCursorR()
                    dbCursorR.updateDbCursor(dbConn)
                    db = dbCursorR.db
                    c:cursors.Cursor = dbCursorR.cursor
                res = a_func(db=None, c=c, *args, **kwargs)
                print("res:",res)
                c.close()
                db.close()
                return res
            return wrapTheFunction
        except:
            log.error("yzlog traceback withSqlite:", traceback.format_exc())
    return B1
def WithCertainRedis(connFilePath:str=None):
    def B1(a_func):
        try:
            @wraps(a_func)
            def wrapTheFunction(r=None, *args, **kwargs):
                if type(r) == bool:
                    r = None
                if r is None:
                    dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
                    dbCursorR = DbCursorR()
                    dbCursorR.updateRedis(dbConn)
                    r:redis.client.Redis = dbCursorR.r
                res = a_func(r=r, *args, **kwargs)
                print("res:",res)
                r.close()
                return res
            return wrapTheFunction
        except:
            log.error("yzlog traceback withSqlite:", traceback.format_exc())
    return B1
def WithCertainMySqlRedis(connFilePath:str=None):
    def B1(a_func):
        try:
            @wraps(a_func)
            def wrapTheFunction(db=None, c:cursors.Cursor=None, r=None, *args, **kwargs):
                if type(r) == bool:
                    r = None
                if r is None:
                    dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
                    dbCursorR = DbCursorR(dbConn)
                    db = dbCursorR.db
                    c: cursors.Cursor = dbCursorR.cursor
                    r:redis.client.Redis = dbCursorR.r
                res = a_func(db=db, c=c, r=r, *args, **kwargs)
                print("res:",res)
                c.close()
                db.close()
                r.close()
                return res
            return wrapTheFunction
        except:
            log.error("yzlog traceback withSqlite:", traceback.format_exc())
    return B1
def WithPopFileDialog(fileFlag:str=None):
    def B1(a_func):
        try:
            @wraps(a_func)
            def wrapTheFunction(filePath:str, *args, **kwargs):
                dir = SqliteUtill.selectValue(table="dirs",key=fileFlag,dbName="preference.sqlite")
                if dir is not None and dir != "":
                    dir = "file:///" + dir
                else:
                    dir = "file:///" + "C:/"
                urls = QFileDialog.getOpenFileUrls(None, caption="选择文件", directory=QUrl(dir))
                pass  # 2204# print("yzlog urls2:", urls)
                if urls[0]:
                    filePathArr = []
                    for qUrl in urls[0]:
                        filePath = qUrl.toString().replace("file:///", "")
                        filePathArr.append(filePath)
                    dirPath, fileName = os.path.split(urls[0][0].toString().replace("file:///", ""))
                    filePath = os.path.join(dirPath,fileName)
                    SqliteUtill.updateValue(table="dirs", key=fileFlag, value=dirPath, dbName="preference.sqlite")
                    print("yzprt dirPath,fileName:", filePath, dirPath, fileName)
                    res = a_func(filePath,*args, **kwargs)
                    return res
            return wrapTheFunction
        except:
            log.error("yzlog traceback withSqlite:", traceback.format_exc())
    return B1
# 若connPath没有在方法调用时显式指出,即connPath="abc",则默认为args的第一个位置
def WithUncertainSqlite(a_func):
    try:
        @wraps(a_func)
        def wrapTheFunction(connPath:str,conn=None, c=None, *args, **kwargs):
            # if 'connPath' in kwargs and kwargs['connPath'] is not None:
            #     connPath = kwargs['connPath']
            # elif args is not None and len(args)>0:
            #     connPath = args[0]
            if conn is None or c is None:
                conn = sqlite3.connect(DirUtil.getDefaultDb() if connPath is None else DirUtil.upperIfNotExists(connPath))
                c = conn.cursor()
            res = a_func(connPath=connPath, conn=None, c=c, *args, **kwargs)
            print("res:",res)
            # print(res)
            conn.commit()
            conn.close()
            return res
        return wrapTheFunction
    except:
        log.error("yzlog traceback withSqlite:", traceback.format_exc())
def WithUncertainMySql(a_func):
    try:
        @wraps(a_func)
        def wrapTheFunction(connFilePath:str,db=None, c=None, *args, **kwargs):
            if db is None or c is None:
                dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
                dbCursorR = DbCursorR()
                dbCursorR.updateDbCursor(dbConn)
                db = dbCursorR.db
                c: cursors.Cursor = dbCursorR.cursor
            res = a_func(connFilePath=connFilePath,db=db,c=c, *args, **kwargs)
            print("res:",res)
            c.close()
            db.close()
            return res
        return wrapTheFunction
    except:
        log.error("yzlog traceback withSqlite:", traceback.format_exc())
def WithUncertainRedis(a_func):
    try:
        @wraps(a_func)
        def wrapTheFunction(connFilePath:str,r=None, *args, **kwargs):
            if type(r) == bool:
                r = None
            if r is None:
                dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
                dbCursorR = DbCursorR()
                dbCursorR.updateRedis(dbConn)
                r: redis.client.Redis = dbCursorR.r
            res = a_func(connFilePath=connFilePath, r=r, *args, **kwargs)
            print("res:", res)
            r.close()
            return res

        return wrapTheFunction
    except:
        log.error("yzlog traceback withSqlite:", traceback.format_exc())
# def TryPassParam(a_func):
#     def B1():
#         try:
#             conn = sqlite3.connect(r'Raww/dap.db')
#         except:
#             log.error("yzlog traceback.format_exc():", traceback.format_exc())
#         @wraps(a_func)
#         def wrapTheFunction(*args, **kwargs):
#             res = a_func(*args, **kwargs)
#             conn.commit()
#             conn.close()
#             return res
#         return wrapTheFunction
#     return B1
# def TryPassParam2(a_func):
#     try:
#         conn = sqlite3.connect(r'Raww/dap.db')
#     except:
#         log.error("yzlog traceback.format_exc():", traceback.format_exc())
#     @wraps(a_func)
#     def B1(*args, **kwargs):
#         res = a_func(param=conn,*args, **kwargs)
#         print(res)
#         conn.commit()
#         conn.close()
#         return res
#     return B1

def TryExcept2(returnDefault=None):
    def B1(a_func):
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            try:
                res = a_func(*args, **kwargs)
                return res
            except:
                log.info("yzlog traceback TryExcept:", traceback.format_exc())
                return returnDefault

        return wrapTheFunction
    return B1
def TryExcept(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        try:
            res = a_func(*args, **kwargs)
            return res
        except:
            log.info("yzlog traceback TryExcept:", traceback.format_exc())
            soundFilePath = os.path.join(DirUtil.getDesktopDir(), r"car_laba.wav")
            print("yzprt soundFilePath:", soundFilePath)
            cdPS = 10
            while cdPS>0:
                playsound(soundFilePath)
                cdPS-=1
                sleep(1)
            return None
    return wrapTheFunction
def TryExceptWithCustomMsg(errorMsg=None,returnDefault=None):
    def B1(a_func):
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            try:
                res = a_func(*args, **kwargs)
                return res
            except:
                log.info("yzlog traceback TryExcept:", traceback.format_exc())
                if errorMsg is not None:
                    log.info("yzlog TryExcept errorMsg:", errorMsg)
                return returnDefault
        return wrapTheFunction
    return B1
def Async(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        thread30 = threading.Thread(name='t30', target=a_func, args=(*args,), daemon=True) # kwargs=(**kwargs,),
        thread30.start()
        # 最多等thread30在5秒内执行完毕,超时的话就强制终止thread30线程了,也可用time.sleep来固定等它5秒
        thread30.join(86400)
    return wrapTheFunction
def AsyncNoJoin(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        thread31 = threading.Thread(name='t31', target=a_func, args=(*args,), daemon=True)  # kwargs=(**kwargs,),
        thread31.start()
    return wrapTheFunction


# def Retry(a_func,countDown=3,slpSeconds=5,retryIfRes=None):
#     @wraps(a_func)
#     def wrapTheFunction(*args, **kwargs):
#         for i in range(countDown):
#             try:
#                 res = a_func(*args, **kwargs)
#                 if res == retryIfRes:
#                     print("Not expected", "countDown:", countDown-i)
#                     sleep(slpSeconds)
#                 else:
#                     return res
#             except:
#                 print("Abnormal", "countDown:", countDown-i)
#                 sleep(slpSeconds)
#         return None
#     return wrapTheFunction
def CheckPath(pathArr=[]):
    def CheckPathInner(a_func):
        @wraps(a_func)
        def wrapTheFunction(*args, **kwargs):
            # log.info("*args:", *args)
            # log.info("**kwargs:", **kwargs)
            # log.info("kwargs:", kwargs)
            # for k,vv in kwargs.items():
            #     log.info(k,vv)
            for i in range(len(pathArr)):
                pathFile = kwargs.get(pathArr[i])
                if None!=pathFile:
                    path,fileName = os.path.split(pathFile)
                    # log.info(pathArr[i],path,type(path),os.path.isdir(path))
                    if not os.path.exists(path):
                        os.makedirs(path)
            res = a_func(*args, **kwargs)
            return res
        return wrapTheFunction
    return CheckPathInner
def RecordSthToTxt():
    pass
# @TryPassParam
# def testPassParam(param):
#     log.info("yzlog param:", param)
#     print("executed")
#     return 55

# Test:
@WithSqlite
def testSqlLock(sqlStr:str,conn=None,c=None):
    res = c.execute(sqlStr).fetchall()
    return res
@WithCertainSqlite(connPath="./raw/connections/preference.sqlite")
def testSqliteWithCertainConnPath(conn=None, c:sqlite3.Cursor=None):
    res = c.execute("select * from dirs order by id desc limit 1").fetchall()
    return res
@WithUncertainSqlite
def testSqliteWithUncertainConnPath(conn=None, c:sqlite3.Cursor=None, connPath=None):
    res = c.execute("select * from dirs order by id desc limit 1").fetchall()
    return res
@WithUncertainSqlite
def testSqliteWithUncertainConnPath2(connPath,conn=None, c:sqlite3.Cursor=None):
    res = c.execute("select * from dirs order by id desc limit 1").fetchall()
    return res
@WithCertainMySql(connFilePath=DBCNF_DIF)
def testCertainMysql(db=None, c:cursors.Cursor=None):
    c.execute("select * from app_user order by id asc limit 1;")
    res = c.fetchall()
    print("yzprt res:", res)
    return res
@WithUncertainMySql
def testUncertainMysql(connFilePath:str, db=None, c:cursors.Cursor=None):
    c.execute("select * from app_user order by id asc limit 1;")
    res = c.fetchall()
    print("yzprt res:", res)
    return res
@WithCertainRedis(connFilePath=DBCNF_DIF)
def testCertainRedis(r:redis.client.Redis=None):
    res = r.get("app_base_config:rollCnf:ACC_PaceX")
    print("yzprt res:", res)
    return res
@WithUncertainRedis
def testUncertainRedis(connFilePath:str, r:redis.client.Redis=None):
    res = r.get("app_base_config:rollCnf:ACC_PaceX")
    print("yzprt res:", res)
    return res

if __name__ == '__main__':
    pass
    # testSqlLock("select * from users where nickname='神谷子'")
    pass
    # connPath ="./raw/connections/preference.sqlite"
    # conn = sqlite3.connect(DirUtil.getDefaultDb() if connPath is None else DirUtil.upperIfNotExists(connPath))
    # c = conn.cursor()
    # testSqliteWithCertainConnPath()
    # testSqliteWithCertainConnPath(conn=conn,c=c)
    # testSqliteWithUncertainConnPath(connPath="./raw/connections/preference.sqlite")
    # testSqliteWithUncertainConnPath(connPath)
    # testSqliteWithUncertainConnPath2("./raw/connections/preference.sqlite")
    pass
    connFilePath=DBCNF_DIF
    dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
    dbCursorR = DbCursorR()
    dbCursorR.updateDbCursor(dbConn)
    db = dbCursorR.db
    c: cursors.Cursor = dbCursorR.cursor
    # testCertainMysql()
    # testUncertainMysql(connFilePath,db=db,c=c)
    pass
    connFilePath=DBCNF_DIF
    dbConn = getDbConnByFile(DirUtil.upperIfNotExists(connFilePath))
    dbCursorR = DbCursorR()
    dbCursorR.updateRedis(dbConn)
    r = dbCursorR.r
    testCertainRedis()
    # testUncertainRedis(connFilePath=connFilePath,r=r)
    # log.info(ShowTimeCost.__name__)
    # dir = os.path.join(os.path.dirname(__file__),"..")
    # log.info("yzlog dir:", dir)
    # dir2 = os.path.abspath(__file__)
    # log.info("yzlog dir2:", dir2)
    # dir3 = os.path.dirname(os.path.abspath(__file__))
    # log.info("yzlog dir3:", dir3)
    # try:
    #     conn = sqlite3.connect(DirUtil.getDapDir())
    #     conn.commit()
    #     conn.close()
    # except:
    #     log.error("yzlog traceback.format_exc():", traceback.format_exc())
    # testPassParam()

